// Copyright 2019 Proyectos y Sistemas de Mantenimiento SL (eProsima).
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// ------------------------------------------------------------------
// Modification history:
// feature: IPC transport adapt new transport
// feature: Unified Transport Abstraction Layer Unified Transport Abstraction Layer
// ------------------------------------------------------------------

#ifndef INCLUDE_VBS_TRANSPORT_TRANSPORTINTERFACE_H_
#define INCLUDE_VBS_TRANSPORT_TRANSPORTINTERFACE_H_

#include <ertps/utils/LocatorAlias.h>
#include <xmlparser/xmlutils/PortParameters.h>

#include <transport/TransportDescriptorInterface.h>
#include <transport/SenderResource.h>

#include <memory>
#include <vector>

using namespace vbsutil::xmlparser;

namespace vbs {
namespace transport {
class ReceiverResource;
class Transport;
//! Default maximum message size
constexpr uint32_t s_maximumMessageSize = 65500U;
//! Default maximum initial peers range
constexpr uint32_t s_maximumInitialPeersRange = 4U;
//! Default minimum socket buffer
constexpr uint32_t s_minimumSocketBuffer = 65536U;
//! Default IPv4 address
static const std::string s_IPv4AddressAny = "0.0.0.0";
//! Default IPv6 address
static const std::string s_IPv6AddressAny = "::";

using SendResourceList = std::vector<std::unique_ptr<vbs::transport::SenderResource>>;

/**
 * Interface against which to implement a transport layer, decoupled from ERTPS internals.
 * TransportInterface expects the user to implement a logical equivalence between Locators and
 * protocol-specific "channels". This equivalence can be narrowing: For instance in UDP/IP, a port
 * would take the role of channel, and several different locators can map to the same port, and
 * hence the same channel.
 * @ingroup TRANSPORT_MODULE
 * */
class RTPS_DllAPI TransportInterface {
 public:
    /**
     * Aside from the API defined here, an user-defined Transport must define a descriptor data type
     * and a constructor that expects a constant reference to such descriptor. e.g:
     *
     * class MyTransport:
     * public:
     *    typedef struct { ... } MyTransportDescriptor;
     *    MyTransport(const MyTransportDescriptor&);
     *    ...
     */
    virtual ~TransportInterface() = default;

    //! Copy constructor
    TransportInterface(const TransportInterface& t) = delete;

    //! Copy assignment
    TransportInterface& operator=(const TransportInterface& t) = delete;

    //! Move constructor
    TransportInterface(TransportInterface&& t) = delete;

    //! Move assignment
    TransportInterface& operator=(TransportInterface&& t) = delete;

    /**
     * Initialize this transport. This method will prepare all the internals of the transport.
     * @param properties Optional policy to specify additional parameters of the created transport.
     * @return True when the transport was correctly initialized.
     */
    virtual bool init() = 0;

    /**
     * Must report whether the input channel associated to this locator is open. Channels must
     * either be fully closed or fully open, so that "open" and "close" operations are whole and
     * definitive.
     */
    virtual bool IsInputChannelOpen(const Locator&) const = 0;

    //! Must report whether the given locator is supported by this transport (typically inspecting
    //! its "kind" value).
    virtual bool IsLocatorSupported(const Locator&) const = 0;

    //! Must report whether the given locator is allowed by this transport.
    virtual bool is_locator_allowed(const Locator&) const = 0;

    //! Must open the channel that maps to/from the given locator. This method must allocate,
    //! reserve and mark any resources that are needed for said channel.
    virtual bool OpenOutputChannel(SendResourceList&, const Locator&) { return true; }

    virtual bool OpenOutputChannel(std::map<Locator_t, std::shared_ptr<SenderResource>>&, const Locator&, Locator_t&) {
        return true;
    }
    /** Opens an input channel to receive incoming connections.
     *   If there is an existing channel it registers the receiver interface.
     */
    virtual bool OpenInputChannel(const Locator&, std::shared_ptr<ReceiverResource>, uint32_t) = 0;

#if defined(__linux)
    virtual void shared_mem_clean() {};
#endif

    /**
     * Must close the channel that maps to/from the given locator.
     * IMPORTANT: It MUST be safe to call this method even during a Receive operation on another
     * thread. You must implement any necessary mutual exclusion and timeout mechanisms to make sure
     * the channel can be closed without damage.
     */
    virtual bool CloseInputChannel(const Locator&) = 0;

    //! Must report whether two locators map to the same internal channel.
    virtual bool DoInputLocatorsMatch(const Locator&, const Locator&) const = 0;

    //! Returns the locator describing the main (most general) channel that can write to the
    //! provided remote locator.
    virtual Locator RemoteToMainLocal(const Locator& remote) const {
        if (!IsLocatorSupported(remote)) {
            return false;
        }

        Locator mainLocal(remote);
        mainLocal.set_Invalid_Address();
        return mainLocal;
    }

    /**
     * Transforms a remote locator into a locator optimized for local communications.
     *
     * If the remote locator corresponds to one of the local interfaces, it is converted
     * to the corresponding local address.
     *
     * @param [in]  remote_locator Locator to be converted.
     * @param [out] result_locator Converted locator.
     *
     * @return false if the input locator is not supported/allowed by this transport, true
     * otherwise.
     */
    virtual bool transform_remote_locator(const Locator& remote_locator, Locator& result_locator) const {
        if (IsLocatorSupported(remote_locator)) {
            result_locator = remote_locator;
            return true;
        }
        return false;
    }

    //! Performs locator normalization (assign valid IP if not defined by user)
    virtual LocatorList NormalizeLocator(const Locator& locator) {
        LocatorList list;
        if (IsLocatorSupported(locator)) {
            list.push_back(locator);
        }
        return list;
    }

    virtual bool select_multicast() const { return false; }

    //! Must report whether the given locator is from the local host
    virtual bool is_local_locator(const Locator& locator) const {
        static_cast<void>(locator);
        return false;
    }

    //! Return the transport configuration (Transport Descriptor)
    virtual TransportDescriptorInterface* get_configuration() = 0;

    //! Add default output locator to the locator list
    virtual void AddDefaultOutputLocator(LocatorList& defaultList) { static_cast<void>(defaultList); }

    //! Add metatraffic multicast locator with the given port
    virtual bool getDefaultMetatrafficMulticastLocators(LocatorList& locators,
                                                        uint32_t metatraffic_multicast_port) const {
        static_cast<void>(locators);
        static_cast<void>(metatraffic_multicast_port);
        return true;
    }

    //! Add metatraffic unicast locator with the given port
    virtual bool getDefaultMetatrafficUnicastLocators(LocatorList& locators, uint32_t metatraffic_unicast_port) const {
        static_cast<void>(locators);
        static_cast<void>(metatraffic_unicast_port);
        return true;
    }

    //! Add unicast locator with the given port
    virtual bool getDefaultUnicastLocators(LocatorList& locators, uint32_t unicast_port) const {
        static_cast<void>(locators);
        static_cast<void>(unicast_port);
        return true;
    }

    //! Assign port to the given metatraffic multicast locator if not already defined
    virtual bool fillMetatrafficMulticastLocator(Locator& locator, uint32_t metatraffic_multicast_port) const {
        static_cast<void>(locator);
        static_cast<void>(metatraffic_multicast_port);
        return true;
    }

    //! Assign port to the given metatraffic unicast locator if not already defined
    virtual bool fillMetatrafficUnicastLocator(Locator& locator, uint32_t metatraffic_unicast_port) const {
        static_cast<void>(locator);
        static_cast<void>(metatraffic_unicast_port);
        return true;
    }

    //! Configure the initial peer locators list
    virtual bool configureInitialPeerLocator(Locator& locator, const vbsutil::xmlparser::PortParameters& port_params,
                                             uint32_t domainId, LocatorList& list) const {
        static_cast<void>(locator);
        static_cast<void>(port_params);
        static_cast<void>(domainId);
        static_cast<void>(list);
        return true;
    }

    //! Assign port to the given unicast locator if not already defined
    virtual bool fillUnicastLocator(Locator& locator, uint32_t well_known_port) const {
        static_cast<void>(locator);
        static_cast<void>(well_known_port);
        return true;
    }

    /**
     * @return The maximum datagram size for reception supported by the transport
     */
    virtual uint32_t max_recv_buffer_size() const = 0;

    /**
     * @brief Update network interfaces.
     */
    virtual void update_network_interfaces() {}
    /**
     * Shutdown method to close the connections of the transports.
     */
    virtual void shutdown() {}

    //! Return the transport kind
    int32_t kind() const { return transport_kind_; }

    Transport* get_transport() const { return parent_transport_; }

    void set_transport(Transport* transport) { parent_transport_ = transport; }

 protected:
    TransportInterface(int32_t transport_kind) : transport_kind_(transport_kind), parent_transport_(nullptr) {}

    int32_t transport_kind_;

    Transport* parent_transport_;
};

}  // namespace transport
}  // namespace vbs

#endif  // INCLUDE_VBS_TRANSPORT_TRANSPORTINTERFACE_H_
